package se.kth.ict.id2203.assignment4;

import java.util.Set;

import org.apache.log4j.PropertyConfigurator;

import se.kth.ict.id2203.abortableconsensus.AbortableConsensus;
import se.kth.ict.id2203.abortableconsensus.RWAbortableConsensus;
import se.kth.ict.id2203.abortableconsensus.RWAbortableConsensusInit;
import se.kth.ict.id2203.beb.BEB;
import se.kth.ict.id2203.beb.BEBInit;
import se.kth.ict.id2203.beb.BestEffortBroadcast;
import se.kth.ict.id2203.console.Console;
import se.kth.ict.id2203.console.java.JavaConsole;
import se.kth.ict.id2203.eld.ELD;
import se.kth.ict.id2203.eld.ELDInit;
import se.kth.ict.id2203.eld.EventualLeaderDetector;
import se.kth.ict.id2203.pp2p.PerfectPointToPointLink;
import se.kth.ict.id2203.pp2p.delay.DelayLink;
import se.kth.ict.id2203.pp2p.delay.DelayLinkInit;
import se.kth.ict.id2203.uniformconsensus.PaxosUniformConsensus;
import se.kth.ict.id2203.uniformconsensus.PaxosUniformConsensusInit;
import se.kth.ict.id2203.uniformconsensus.UniformConsensus;
import se.sics.kompics.Component;
import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Fault;
import se.sics.kompics.Handler;
import se.sics.kompics.Kompics;
import se.sics.kompics.address.Address;
import se.sics.kompics.launch.Topology;
import se.sics.kompics.network.Network;
import se.sics.kompics.network.mina.MinaNetwork;
import se.sics.kompics.network.mina.MinaNetworkInit;
import se.sics.kompics.timer.Timer;
import se.sics.kompics.timer.java.JavaTimer;


public class Assignment4Main extends ComponentDefinition {
	
	static {
		PropertyConfigurator.configureAndWatch("log4j.properties");
	}
	
	private static int selfId;
	private static String commandScript;
	
	Topology topology = Topology.load(System.getProperty("topology"), selfId);


	public static void main(String[] args) {
		
		selfId = Integer.parseInt(args[0]);
		commandScript = args[1];

		Kompics.createAndStart(Assignment4Main.class);
	}

	/**
	 * Instantiates a new assignment1 group0.
	 */
	public Assignment4Main() {
		// create components
		Component time = create(JavaTimer.class);
		Component network = create(MinaNetwork.class);
		Component con = create(JavaConsole.class);
		Component pp2p = create(DelayLink.class);
		Component app = create(Application4.class);
		Component beb = create(BEB.class);
		Component ac = create(RWAbortableConsensus.class);
		Component puc = create(PaxosUniformConsensus.class);
		Component eld = create(ELD.class);


		// handle possible faults in the components
		subscribe(handleFault, time.control());
		subscribe(handleFault, network.control());
		subscribe(handleFault, con.control());
		subscribe(handleFault, pp2p.control());
		subscribe(handleFault, app.control());
		subscribe(handleFault, beb.control());
		subscribe(handleFault, ac.control());
		subscribe(handleFault, puc.control());
		subscribe(handleFault, eld.control());
		
		// initialize the components
		Address self = topology.getSelfAddress();
		Set<Address> neighborSet = topology.getNeighbors(self);

		trigger(new MinaNetworkInit(self, 5), network.control());
		trigger(new DelayLinkInit(topology), pp2p.control());
		trigger(new Application4Init(commandScript, neighborSet, self), app
				.control());
		trigger(new BEBInit(topology), beb.control());
		
		trigger(new RWAbortableConsensusInit(topology), ac.control());
		trigger(new PaxosUniformConsensusInit(topology), puc.control());
		trigger(new ELDInit(topology,3000), eld.control());

		// connect the components
		connect(app.required(Console.class),
				con.provided(Console.class));
		
		connect(app.required(UniformConsensus.class),
				puc.provided(UniformConsensus.class));
		
		connect(app.required(Timer.class), 
				time.provided(Timer.class));

		connect(puc.required(BestEffortBroadcast.class),
				beb.provided(BestEffortBroadcast.class));
		
		connect(ac.required(BestEffortBroadcast.class),
				beb.provided(BestEffortBroadcast.class));
		
		connect(puc.required(AbortableConsensus.class),
				ac.provided(AbortableConsensus.class));
		
		connect(puc.required(EventualLeaderDetector.class),
				eld.provided(EventualLeaderDetector.class));
		
		connect(eld.required(Timer.class), 
				time.provided(Timer.class));
		
		connect(eld.required(PerfectPointToPointLink.class),
				pp2p.provided(PerfectPointToPointLink.class));
		
		connect(ac.required(PerfectPointToPointLink.class),
				pp2p.provided(PerfectPointToPointLink.class));
		
		connect(beb.required(PerfectPointToPointLink.class),
				pp2p.provided(PerfectPointToPointLink.class));
		
		connect(pp2p.required(Timer.class), time.provided(Timer.class));
		connect(pp2p.required(Network.class), network.provided(Network.class));

	}

	Handler<Fault> handleFault = new Handler<Fault>() {
		public void handle(Fault fault) {
			fault.getFault().printStackTrace(System.err);
		}
	};
}
